package org.nlp2rdf.navigator.component.store;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import org.apache.log4j.Logger;
import org.dllearner.core.owl.Axiom;
import org.dllearner.core.owl.Description;
import org.dllearner.core.owl.EquivalentClassesAxiom;
import org.dllearner.core.owl.NamedClass;
import org.dllearner.parser.KBParser;
import org.dllearner.parser.ParseException;
import org.dllearner.utilities.owl.OWLAPIAxiomConvertVisitor;
import org.nlp2rdf.navigator.client.common.ConceptDTO;
import org.nlp2rdf.navigator.client.common.StoredConceptDTO;
import org.nlp2rdf.navigator.client.common.RelatedConceptsDTO.StrategyDTO;
import org.nlp2rdf.navigator.component.Component;
import org.nlp2rdf.navigator.component.exception.ComponentInitException;
import org.nlp2rdf.navigator.component.exception.ConceptSavingFailedException;
import org.semanticweb.owlapi.apibinding.OWLManager;
import org.semanticweb.owlapi.model.AddAxiom;
import org.semanticweb.owlapi.model.IRI;
import org.semanticweb.owlapi.model.OWLAxiom;
import org.semanticweb.owlapi.model.OWLClass;
import org.semanticweb.owlapi.model.OWLDataFactory;
import org.semanticweb.owlapi.model.OWLOntology;
import org.semanticweb.owlapi.model.OWLOntologyCreationException;
import org.semanticweb.owlapi.model.OWLOntologyManager;
import org.semanticweb.owlapi.model.RemoveAxiom;

import com.clarkparsia.modularity.IncrementalClassifier;
import com.clarkparsia.modularity.PelletIncremantalReasonerFactory;

public class LogicalRelationStrategy extends Component implements IRelatedConceptStrategy {
	private static final Logger logger = Logger.getLogger(LogicalRelationStrategy.class);

	private String tmpClassName = "http://somethingsomething.darkside/TempClass0815sdlkjdfjdslkfdsjklffdslkjj";

	private MapGuard mapGuard = new MapGuard();

	private class MapGuard {
		private Map<String, StoredConceptDTO> uriToStoredConcepts = new HashMap<String, StoredConceptDTO>();

		public StoredConceptDTO put(StoredConceptDTO value) {
			StoredConceptDTO s = uriToStoredConcepts.put(value.getUri(), value);
			if (s != null) {
				logger.warn("overwrote concept " + s.getLabel() + " " + s.getKbSyntaxString());
			}
			return s;
		}

		public StoredConceptDTO get(String key) {
			return uriToStoredConcepts.get(key);
		}
	}

	private String ontologyFile;
	private OWLDataFactory factory;
	OWLOntologyManager manager;
	OWLOntology ontology;
	IncrementalClassifier incReasoner;

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.nlp2rdf.navigator.component.Component#_init()
	 */
	@Override
	public void _init() throws ComponentInitException {
		try {

			File f = new File(getComponentConfig().getPathModifier() + ontologyFile);
			manager = OWLManager.createOWLOntologyManager();
			factory = manager.getOWLDataFactory();
			ontology = manager.loadOntology(IRI.create(f));

			incReasoner = PelletIncremantalReasonerFactory.getInstance().createNonBufferingReasoner(ontology);
			incReasoner.prepareReasoner();
			logger.debug("Successfully loaded ontology " + getComponentConfig().getPathModifier()
					+ ontologyFile + " NrOfAxioms: " + ontology.getAxiomCount());
		} catch (OWLOntologyCreationException e) {
			throw new ComponentInitException(e);
		} catch (Exception e) {
			throw new ComponentInitException(e);
		}

	}

	public synchronized List<StrategyDTO> getRelatedConcepts(ConceptDTO concept) {
		logger.debug("Getting related concepts for: " + concept.getKbSyntaxString());

		List<StrategyDTO> ret = new ArrayList<StrategyDTO>();

		OWLAxiom target = addClass(concept.getKbSyntaxString(), tmpClassName);
		OWLClass newCl = factory.getOWLClass(IRI.create(tmpClassName));

		Set<OWLClass> superClasses = incReasoner.getSuperClasses(newCl, true).getFlattened();
		Set<OWLClass> subClasses = incReasoner.getSubClasses(newCl, true).getFlattened();
		Set<OWLClass> parallelClasses = new TreeSet<OWLClass>();

		for (OWLClass clazz : superClasses) {
			parallelClasses.addAll(incReasoner.getSubClasses(clazz, true).getFlattened());
		}

		parallelClasses.remove(newCl);

		// remove axiom again
		// (otherwise internal DL-Learner state would be corrupt!)
		remove(target);

		logger.debug("Found SUPER " + superClasses.size() + " " + superClasses);
		logger.debug("Found SUB " + subClasses.size() + " " + subClasses);
		logger.debug("Found PARALLEL " + parallelClasses.size() + " " + parallelClasses);

		ret.add(getStrategy("subclasses", subClasses));
		ret.add(getStrategy("parallel classes", parallelClasses));
		ret.add(getStrategy("super classes ", superClasses));

		return ret;
	}

	private StrategyDTO getStrategy(String title, Set<OWLClass> owlClasses) {
		StrategyDTO s = new StrategyDTO();
		s.setTitle(title);

		for (OWLClass owlClass : owlClasses) {
			StoredConceptDTO stored = mapGuard.get(owlClass.getIRI().toString());
			// logger.debug(owlClass + "" + stored);
			// TODO untested
			if (stored != null) {
				s.addConcept(stored);
			} else {
				ConceptDTO c = new ConceptDTO("(\"" + owlClass.getIRI().toString() + "\")", owlClass.getIRI()
						.toString());
				s.addConcept(c);
			}
		}
		logger.error("made Strategy " + s);
		return s;
	}

	private void remove(OWLAxiom target) {
		int before = ontology.getAxiomCount();
		manager.applyChange(new RemoveAxiom(ontology, target));
		incReasoner.prepareReasoner();
		int after = ontology.getAxiomCount();
		if (before - 1 != after) {
			logger.warn("Problem with removing axiom: Count before was: " + before + " after was: " + after
					+ " Axiom:" + target);
		}

	}

	private OWLAxiom addClass(String learningResultDTO, String classUri) {
		check();
		OWLAxiom target = null;
		try {
			Description d = KBParser.parseConcept(learningResultDTO);

			Axiom a = new EquivalentClassesAxiom(new NamedClass(classUri), d);
			target = OWLAPIAxiomConvertVisitor.convertAxiom(a);

			manager.applyChange(new AddAxiom(ontology, target));

			logger.debug("Added axiom " + classUri + " == " + learningResultDTO);
			incReasoner.prepareReasoner();
			// incReasoner.isConsistent();

		} catch (ParseException e) {
			logger.error(learningResultDTO + " " + classUri, e);
			throw new RuntimeException(e);

		} catch (Exception e) {
			logger.error(learningResultDTO + " " + classUri, e);
			throw new RuntimeException(e);

		} catch (Error e) {
			logger.error(learningResultDTO + " " + classUri, e);
			throw new RuntimeException(e);
		}

		return target;

	}

	public void update(StoredConceptDTO learningResultDTO) throws ConceptSavingFailedException {
		check();
		try {
			mapGuard.put(learningResultDTO);
			addClass(learningResultDTO.getKbSyntaxString(), learningResultDTO.getUri());
		} catch (Exception e) {
			String msg = "updating internal reasoner failed: " + learningResultDTO.toDebugString();
			logger.error(msg, e);
			throw new ConceptSavingFailedException(msg, e);
		}

	}

	public void update(List<StoredConceptDTO> learningResultDTOs) throws ConceptSavingFailedException {
		for (StoredConceptDTO learningResultDTO : learningResultDTOs) {
			update(learningResultDTO);
		}
	}

	public String getOntologyFile() {
		return ontologyFile;
	}

	public void setOntologyFile(String ontologyFile) {
		this.ontologyFile = ontologyFile;
	}

}
